﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;

namespace SocketWeaver.FixedMath
{
    [Serializable]
    public struct FTransformInterpolation
    {
        public FTransformData t1;
        public FTransformData t2;
        public FTransform fTransform;
        public Transform transform;
    }

    [Serializable]
    public struct FTransformData
    {
        public Vector3 position;
        public Quaternion rotation;
        public Vector3 localScale;
        public bool ready;
    }

    [DefaultExecutionOrder(10000)]
    public class FTransformInterpolationManager : MonoBehaviour
    {
        public bool setFixedDeltaTime;
        public List<FTransformInterpolation> interpolations = new List<FTransformInterpolation>();
        public float fixedDeltaTime;
        public Stopwatch stopWatch;
        public long interpolationMS;

        public void Awake()
        {
            FTransform[] fts = FindObjectsOfType<FTransform>();

            for(int i = 0; i < fts.Length; i++)
            {
                if(fts[i].interpolation)
                {
                    FTransformInterpolation interpolation = new FTransformInterpolation();
                    interpolation.fTransform = fts[i];
                    interpolation.transform = fts[i].transform;
                    interpolations.Add(interpolation);
                }
            }

            if(setFixedDeltaTime)
            {
                Time.fixedDeltaTime = fixedDeltaTime;
            }

            stopWatch = new Stopwatch();
            stopWatch.Start();
        }

        public void OnDestroy()
        {
            stopWatch.Stop();
        }

        private void FixedUpdate()
        {
            UpdateInterpolationData();
            interpolationMS = stopWatch.ElapsedMilliseconds;
        }

        public void UpdateInterpolationData()
        {
            for(int i = 0; i < interpolations.Count; i++)
            {
                FTransformInterpolation interpolation = interpolations[i];
                if(interpolation.t1.ready && interpolation.t2.ready)
                {
                    interpolation.t1 = interpolation.t2;
                    interpolation.t2.ready = true;
                    interpolation.t2.position = (Vector3)interpolation.fTransform.position;
                    interpolation.t2.rotation = (Quaternion)interpolation.fTransform.rotation;
                    interpolation.t2.localScale = (Vector3)interpolation.fTransform.localScale;
                }
                else if(interpolation.t1.ready && !interpolation.t2.ready)
                {
                    interpolation.t2.ready = true;
                    interpolation.t2.position = (Vector3)interpolation.fTransform.position;
                    interpolation.t2.rotation = (Quaternion)interpolation.fTransform.rotation;
                    interpolation.t2.localScale = (Vector3)interpolation.fTransform.localScale;
                }
                else
                {
                    interpolation.t1.ready = true;
                    interpolation.t1.position = (Vector3)interpolation.fTransform.position;
                    interpolation.t1.rotation = (Quaternion)interpolation.fTransform.rotation;
                    interpolation.t1.localScale = (Vector3)interpolation.fTransform.localScale;
                }
                interpolations[i] = interpolation;
            }
        }

        public void Update()
        {
            long currentElapsed = stopWatch.ElapsedMilliseconds;
            long delta = currentElapsed - interpolationMS;
            float interpolationRatio = delta / (fixedDeltaTime * 1000);

            for (int i = 0; i < interpolations.Count; i++)
            {
                FTransformInterpolation interpolation = interpolations[i];

                if (interpolation.t1.ready && interpolation.t2.ready)
                {
                    interpolation.transform.position = Vector3.Lerp(interpolation.t1.position, interpolation.t2.position, interpolationRatio);
                    interpolation.transform.rotation = Quaternion.Slerp(interpolation.t1.rotation, interpolation.t2.rotation, interpolationRatio);
                    interpolation.transform.localScale = Vector3.Lerp(interpolation.t1.localScale, interpolation.t2.localScale, interpolationRatio);
                }
                else if (interpolation.t1.ready && !interpolation.t2.ready)
                {
                    interpolation.transform.position = interpolation.t1.position;
                    interpolation.transform.rotation = interpolation.t1.rotation;
                    interpolation.transform.localScale = interpolation.t1.localScale;
                }
            }
        }
    }
}
